package edu.calvin.djs32.cs214.interpreters.frontend;

import static org.norecess.antlr.Assert.assertTree;

import org.junit.Before;
import org.junit.Test;
import org.norecess.antlr.ANTLRTester;


public class Hobbes2008ParserTest {
	private ANTLRTester myTester;

	@Before
	public void setUp() {
		myTester = new ANTLRTester(new Hobbes2008FrontEnd());
	}
	
	@Test
	public void shouldRecognizeAnIntegerAsAPRogram() {
		assertTree(Hobbes2008Parser.INTEGER, "(123)", myTester.scanInput("123").parseAs("program"));
		assertTree(Hobbes2008Parser.INTEGER, "(+123)", myTester.scanInput("+123").parseAs("program"));
	    assertTree(Hobbes2008Parser.INTEGER, "(1563)", myTester.scanInput("1563").parseAs("program"));
	    assertTree(Hobbes2008Parser.INTEGER, "(-0x123f)", myTester.scanInput("-0x123f").parseAs("program"));
	}
	
	@Test(expected = Exception.class)
	public void shouldFailWithTwoIntegersAsAPRogram() {
		myTester.scanInput("123 456").parseAs("program");
	}
	@Test
	public void shouldRecognizeStrings(){
		assertTree(Hobbes2008Parser.STRING, "('foo')", myTester.scanInput("'foo'").parseAs("program"));
		assertTree(Hobbes2008Parser.STRING, "('foo bar')", myTester.scanInput("'foo bar'").parseAs("program"));
		assertTree(Hobbes2008Parser.STRING, "(' ')", myTester.scanInput("' '").parseAs("program"));
		assertTree(Hobbes2008Parser.STRING, "('6 foo')", myTester.scanInput("'6 foo'").parseAs("program"));
	}
	
	@Test(expected = Exception.class)
	public void shouldFailWithTwoStringsAsAProgram(){
		myTester.scanInput("'hello' 'goodbye'").parseAs("program");
	}
	@Test
	public void shouldRecognizeATupleAsAProgram() {
	  assertTree(Hobbes2008Parser.TUPLE, "(TUPLE)", myTester.scanInput("()").parseAs("program"));
	  assertTree(Hobbes2008Parser.TUPLE, "(TUPLE(1))", myTester.scanInput("(1)").parseAs("program"));
	  assertTree(Hobbes2008Parser.TUPLE, "(TUPLE(1)(2)(3))", myTester.scanInput("(1, 2, 3)").parseAs("program"));
	}
	
	@Test(expected = Exception.class)
	public void shouldFailOnInitialComma() {
	  myTester.scanInput("(,2, 3)").parseAs("program");
	}
	
	@Test(expected = Exception.class)
	public void shouldFailOnTrailingComma() {
	  myTester.scanInput("(,2, 3,)").parseAs("program");
	}
	
	@Test(expected = Exception.class)
	public void shouldFailOnDoubleComma() {
	  myTester.scanInput("(,2, ,3,)").parseAs("program");
	}
	
	@Test
	public void shouldRecognizeTuplesWithDifferentDataTypes() {
	  assertTree("should recognize tuple of integers",
	      Hobbes2008Parser.TUPLE, "(TUPLE(1)(2)(3))", myTester.scanInput(
	          "(1,2,3)").parseAs("program"));
	  assertTree("should recognize tuple of strings", Hobbes2008Parser.TUPLE,
	      "(TUPLE('a')('b')('c'))", myTester.scanInput("('a','b','c')")
	          .parseAs("program"));
	  assertTree("should recognize a tuple of a tuple",
	      Hobbes2008Parser.TUPLE, "(TUPLE(TUPLE))", myTester.scanInput(
	          "(())").parseAs("program"));
	  assertTree("should recognize a tuple of mixed types",
	      Hobbes2008Parser.TUPLE, "(TUPLE(1)(TUPLE(5))('c'))", myTester
	          .scanInput("(1,(5),'c')").parseAs("program"));
	}
	
	@Test
	public void IdentifiersShouldWork(){
		assertTree("should do regular application", Hobbes2008Parser.APPLICATION, "(APPLICATION(+)(TUPLE(1)(2)))", myTester.scanInput("+(1,2)").parseAs("program"));
		assertTree("should do regular application", Hobbes2008Parser.APPLICATION, "(APPLICATION(-)(TUPLE(1)(2)))", myTester.scanInput("-(1,2)").parseAs("program"));
		assertTree("should do nested tuples", Hobbes2008Parser.APPLICATION, "(APPLICATION(+)(TUPLE(1)(APPLICATION(*)(TUPLE(2)(3)))))", myTester.scanInput("+(1,*(2,3))").parseAs("program"));
	}
	
	@Test
	public void shouldRecognizeIfs(){
		assertTree(Hobbes2008Parser.IF, "(if(1)(2)(3))",myTester.scanInput("if 1 then 2 else 3 end").parseAs("expression"));
		assertTree(Hobbes2008Parser.IF, "(if(1)(if(1)(2)(3))(3))",myTester.scanInput("if 1 then if 1 then 2 else 3 end else 3 end").parseAs("expression"));
		assertTree(Hobbes2008Parser.IF, "(if(1)('foo')(3))",myTester.scanInput("if 1 then 'foo' else 3 end").parseAs("expression"));
		assertTree(Hobbes2008Parser.IF, "(if(1)('foo')('bar'))",myTester.scanInput("if 1 then 'foo' else 'bar' end").parseAs("expression"));
	}
}
